Skip to content

feat(connections): tenant-wide connection sharing — admin provisions, all use - #1018

Merged
alfredo1996 merged 1 commit into
release/1.1from
feat/issue-901-connection-visibility
Jun 11, 2026
Merged

feat(connections): tenant-wide connection sharing — admin provisions, all use#1018
alfredo1996 merged 1 commit into
release/1.1from
feat/issue-901-connection-visibility

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented Jun 11, 2026

Copy link
Copy Markdown
Owner

What

Closes #901 (charter decision: admin provisions, all use — the Metabase/Superset/Looker model).

Today every creator must own a copy of every connection, meaning admins hand out database credentials. This PR adds a visibility flag (private | shared) so an admin can share a connection workspace-wide: everyone in the tenant can query it and build dashboards on it, while credentials never leave the server and management stays owner/admin-only.

Behavior

Surface private (default) shared
/api/query fast path owner only every tenant user
Connections list / GET [id] owner + admin everyone (metadata only, isOwner flag, never the owner id)
Schema/databases introspection owner everyone (editor autocomplete works)
Edit / delete / test / share toggle owner + admin owner + admin only — others see a read-only card
Visibility changes admin-only, owner-scoped

Tests

Verification

🤖 Generated with Claude Code

Summary by CodeRabbit

Release Notes

  • New Features
    • Connection sharing: Team members can now share database connections with the workspace, enabling colleagues to query shared connections read-only.
    • Ownership and permissions: Connection owners control sharing status and management actions; non-owners access shared connections as read-only.
    • "Shared" badge displays on shared connection cards for easy identification.

… all use (#901)

Connections gain a visibility flag (private | shared):

- shared connections are first-class queryable for every tenant user
  (query route fast path) and visible in the connections list and
  schema/databases introspection — creators no longer need their own
  copy of every credential
- credentials never leave the server for anyone; the owner id is
  exposed only as an isOwner boolean
- visibility changes are admin-only and owner-scoped (admins share
  connections they own — the 'admin provisions' model)
- non-owners see shared connections read-only: no edit/delete/test/
  share menu at all
- ConnectionCard gains a Shared badge and a visibility toggle item
  (pure UI props, no business logic in the component package)

Migration 0008 adds the enum column (default private — no behavior
change for existing rows).

E2E: connection-sharing.spec walks the full lifecycle — private
invisibility, admin share, creator read-only view + direct query,
non-admin toggle rejection (403), unshare dropping the creator back
to dashboard-bound access (403 via #972).

Per the 2026-06-11 overnight charter decision: 'Admin provisions, all use'.

Closes #901

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@alfredo1996 alfredo1996 added enhancement New feature or request security Security-related issue pkg:app Next.js application package pkg:component UI component library area:auth Authentication & authorization area:connectors Database connectors labels Jun 11, 2026
@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 77d4b5b6-4b8d-44c1-982f-6d62c238b7ac

📥 Commits

Reviewing files that changed from the base of the PR and between 42fac58 and 540976d.

⛔ Files ignored due to path filters (3)
  • app/drizzle/migrations/0008_lumpy_jubilee.sql is excluded by !app/drizzle/migrations/**
  • app/drizzle/migrations/meta/0008_snapshot.json is excluded by !app/drizzle/migrations/**
  • app/drizzle/migrations/meta/_journal.json is excluded by !app/drizzle/migrations/**
📒 Files selected for processing (13)
  • app/e2e/connection-sharing.spec.ts
  • app/src/app/(dashboard)/connections/page.tsx
  • app/src/app/api/connections/[id]/__tests__/route.test.ts
  • app/src/app/api/connections/[id]/databases/route.ts
  • app/src/app/api/connections/[id]/route.ts
  • app/src/app/api/connections/[id]/schema/route.ts
  • app/src/app/api/connections/__tests__/route.test.ts
  • app/src/app/api/connections/route.ts
  • app/src/app/api/query/route.ts
  • app/src/hooks/use-connections.ts
  • app/src/lib/db/schema.ts
  • app/src/lib/shared/schemas.ts
  • component/src/components/composed/connection-card.tsx

Walkthrough

This PR implements Option B from issue #901: a per-connection visibility model. Connections default to "private" (owner/admin-only) and admins can mark them "shared" to allow all tenant users read-only query and dashboard access. Non-owners cannot modify visibility; isOwner is derived server-side to prevent raw ownerId exposure.

Changes

Connection visibility and tenant sharing

Layer / File(s) Summary
Database schema and type contracts
app/src/lib/db/schema.ts, app/src/lib/shared/schemas.ts, app/src/hooks/use-connections.ts
Adds connectionVisibilityEnum ("private" / "shared") to the connections table with "private" default; updates ConnectionListItem to include visibility and isOwner boolean; extends UpdateConnectionInput and validation schema to support optional admin-only visibility field.
API authorization and response shaping across routes
app/src/app/api/connections/route.ts, app/src/app/api/connections/[id]/route.ts, app/src/app/api/connections/[id]/databases/route.ts, app/src/app/api/connections/[id]/schema/route.ts, app/src/app/api/query/route.ts
All routes now authorize access for both owned and shared (visibility="shared") connections within the tenant; GET routes select visibility and ownerId, reshape responses to expose isOwner instead of raw ownerId, and return via apiList; PATCH blocks non-admin visibility updates with 403 forbidden.
ConnectionCard component UI
component/src/components/composed/connection-card.tsx
Adds shared boolean prop to conditionally render "Shared" badge with Users icon; introduces onToggleVisibility callback and toggleVisibilityLabel ("Share with workspace" default) to add a dropdown menu item for toggling visibility.
Connections page integration
app/src/app/(dashboard)/connections/page.tsx
Derives isAdmin from session; gates onTest, onEdit, onDelete, onDuplicate, and visibility toggle callbacks on c.isOwner || isAdmin, providing read-only UI for non-owners of shared connections.
API authorization and response tests
app/src/app/api/connections/[id]/__tests__/route.test.ts, app/src/app/api/connections/__tests__/route.test.ts
Validates GET response metadata (isOwner derived, ownerId omitted), non-owner access to tenant-shared connections, admin-only PATCH visibility enforcement (non-admin → 403), and admin visibility updates with payload verification.
E2E workflow test
app/e2e/connection-sharing.spec.ts
Full serial lifecycle test: private visibility hides from non-owner, share toggle shows "Shared" badge, non-owner sees read-only card without action menu, non-owner can query shared connection but cannot PATCH visibility, unshare revokes read access with 403 fallback.

Sequence Diagram(s)

sequenceDiagram
    participant Admin as Admin (ALICE)
    participant NonOwner as Non-owner (BOB)
    participant UI
    participant API as GET /api/connections
    participant DB
    
    Admin->>UI: private connection created
    NonOwner->>API: list connections (tenantId=T)
    API->>DB: where (userId=BOB or visibility="shared") and tenantId=T
    DB-->>API: [] (connection not shared yet)
    API-->>NonOwner: empty list
    
    Admin->>UI: click "Share with workspace"
    UI->>API: PATCH /api/connections/[id] {visibility: "shared"}
    API->>DB: UPDATE visibility="shared" where id=X
    DB-->>API: OK
    
    NonOwner->>API: list connections (tenantId=T)
    API->>DB: where (userId=BOB or visibility="shared") and tenantId=T
    DB-->>API: [{id, visibility: "shared", ownerId: ALICE_ID}]
    API->>API: isOwner = (ALICE_ID === BOB) = false
    API-->>NonOwner: [{id, visibility: "shared", isOwner: false}]
    NonOwner->>UI: shows "Shared" badge, read-only
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • alfredo1996/neoboard#633: Extends the /api/connections/[id]/databases route authorization to match the new owner-or-shared pattern introduced in this PR's main implementation.
  • alfredo1996/neoboard#579: Prior work on the /api/query fast-path connection authorization; this PR builds on that by adding the visibility="shared" branch to the same query checkpoint.

Suggested labels

pkg:connection, testing

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/issue-901-connection-visibility

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@alfredo1996
alfredo1996 merged commit 6752289 into release/1.1 Jun 11, 2026
10 of 11 checks passed
@alfredo1996
alfredo1996 deleted the feat/issue-901-connection-visibility branch June 11, 2026 03:04
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area:auth Authentication & authorization area:connectors Database connectors enhancement New feature or request pkg:app Next.js application package pkg:component UI component library security Security-related issue

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants